package codeflyer.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import codeFlyer.web.api.HttpRequest;
import codeFlyer.web.api.HttpResponse;
import codeFlyer.web.api.TemplateEngine;
import codeflyer.server.classload.ClassLoad;
import codeflyer.server.utils.FileSearch;
import codeflyer.server.utils.GetInternetIp;

/**
 * @author CodeFlyer
 * @date Apr 25, 2020
 * @version 1.0
 */

//响应的类型
//如果出现了比如输入了不存在的路径,返回not found 404错误
//或者说浏览器代码出现异常,把错误以html的方式自动返回给浏览器
//根据请求的信息类决定响应什么
public class Response implements HttpResponse{
	
	private FileInputStream fileInputStream = null;
	private BufferedInputStream bufferedInputStream = null;
	private BufferedOutputStream bufferedOutputStream = null;
	private ByteArrayOutputStream byteArrayOutputStream = null;
	private String filePath;
	private String contentType;
	private final String projectPath = new File("").getAbsolutePath().replaceAll("\\\\", "/");  //当前服务器的绝对路径
	private Socket socket=null;
	private HttpRequest request;
	private String project = null;
	
	public String getProject() {
		return project;
	}

	public void setProject(String project) {
		this.project = project;
	}

	public HttpRequest getRequest() {
		return request;
	}

	public void setRequest(HttpRequest request) {
		this.request = request;
	}

	public String getFilePath() {
		return filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}


	public Socket getSocket() {
		return socket;
	}

	public void setSocket(Socket socket) {
		this.socket = socket;
	}
	
	//路径需要添加斜杠(绝对路径)
	public void redirect(String url) {  //在头部加上一个Location:你要跳转的url		
		try {
				//拼接协议
				StringBuilder sbResponse = new StringBuilder();
				sbResponse.append("HTTP/1.1 301 OK").append("\r\n");
				sbResponse.append("Location:").append("http:/").append("/"+"120.25.220.73"+":"+socket.getLocalPort()).append(url).append("\r\n");
				//响应内容应该根据具体请求动态进行
				sbResponse.append("Content-type:").append("text/html").append("\r\n");  //不是和请求的accept相同,根据你自己的文件选择content-type
				//System.out.println(contentType);
				//sbResponse.append("Content-length:").append(contentLen).append("\r\n");
				sbResponse.append("\r\n");
				System.out.println(sbResponse);
				byte[] bResponse = sbResponse.toString().getBytes();
				//发送给浏览器
				bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
				bufferedOutputStream.write(bResponse);
				bufferedOutputStream.flush();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				if(bufferedOutputStream!=null) {
					bufferedOutputStream.close();
				}				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
	}
	
	//路径需要带斜杆
	public void forward(String url) {                 //请求转发
		System.out.println("请求转发"+url);
		Boolean exist = iterateFile(url);  //检查一下路径是是否正确
		//先要从项目的web_content去查找文件,如果有就不使用webapp下的
		boolean isProject = iterateProjectFile(url);
		//这里没有和项目进行比较
		if(exist == true || isProject == true) {
			if(url.equals("/error/401.html")){
				push("/error/401.html",401,"unauthorized");
			}else if(url.equals("/error/404.html")){
				push("/error/404.html",404,"Not Found");
			}else{
				push(url, 200, "OK");
			}		
		}else {
			push("/error/404.html",404,"Not Found");
		}
	}

	/**
	 * 发送拼接好的响应信息给浏览器
	 * @param socket
	 */
	public void response() {
		
		//存在的问题,我在响应之前要判断,是否路径为/或者我没有这个路径
		//没有路径的情况需要返回404页面,也就是说我要遍历所有资源路径然后进行比较
		//一般来说静态资源的位置是固定的在某个文件夹下
		
		//这里响应页面的页面的原则
		//1如果有项目部署在了服务器,优先响应服务器对应的页面而不是服务器默认页面,需要进行判断如果和服务器有相同页面先响应项目的
		//2如果存在重定向和请求转发应该优先使用重定向和转发的响应
		System.out.println("解析的文件路径: "+filePath);
		Boolean exist = iterateFile(filePath);  //检查一下路径是是否正确
		Boolean pExist = iterateProjectFile(filePath);
		if(exist == true || pExist == true) {
					push(filePath,200,"OK");
			}else if(filePath.equals("/")) { //表示要返回首页
				push("/index.html",200,"OK");
			}else if(filePath.equals("/favicon.ico")){
				push("/favicon/cat3.ico",200,"OK");
			}else {
				push("/error/404.html",404,"Not Found");
			}
			//响应信息image/jpeg
		}
		
	/**
	 * 检测webapp下路径是否存在
	 * @param contentType
	 * @return
	 */
	public Boolean iterateProjectFile(String filePath) {
		FileSearch fileSearch = new FileSearch();
		fileSearch.iterateFile(project+"/web_content");
		List<String> list = fileSearch.getFileList();
		System.out.println(list);
		for (String string : list) {
			String[] tStrings = string.trim().split("web_content");
			System.out.println("我项目的的所有路径"+tStrings[1]);
			if(filePath.equals(tStrings[1])) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 检测项目下web_content路径是否存在
	 * @param contentType
	 * @return
	 */
	public Boolean iterateFile(String filePath) {
		FileSearch fileSearch = new FileSearch();
		fileSearch.iterateFile(projectPath+"/webapp");
		List<String> list = fileSearch.getFileList();
		System.out.println(list);
		for (String string : list) {
			String[] tStrings = string.trim().split("webapp");
			System.out.println("我的所有路径"+tStrings[1]);
			if(filePath.equals(tStrings[1])) {
				return true;
			}
		}
		return false;
	}
	
	//提供一个向页面传递字符串的方法
	public void Writer(String string) {
		try {
			byte[] stringByte = string.getBytes("utf-8");
			//拼接协议
			StringBuilder sbResponse = new StringBuilder();
			sbResponse.append("HTTP/1.1 200 OK").append("\r\n");
			//响应内容应该根据具体请求动态进行
			sbResponse.append("Content-type:").append("text/html").append("\r\n");  //不是和请求的accept相同,根据你自己的文件选择content-type
			//System.out.println(contentType);
			//sbResponse.append("Content-length:").append(contentLen).append("\r\n");
			sbResponse.append("\r\n");
			byte[] bResponse = sbResponse.toString().getBytes();
			
			//不通过字符流的方式发送数据,而是用字节流的方式,字符流会导致字节码数组失真
			//把响应信息和数据的字节数组进行拼接
			byte[] total = new byte[bResponse.length+stringByte.length]; 
			System.arraycopy(bResponse, 0, total, 0, bResponse.length);
			System.arraycopy(stringByte, 0, total, bResponse.length, stringByte.length);
			
			System.out.println("合并的总字节数:"+total.length);
			//发送给浏览器
			bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
			bufferedOutputStream.write(total);
			bufferedOutputStream.flush();
		} catch (IOException e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally {
			try {
				if(bufferedOutputStream!=null) {
					bufferedOutputStream.close();
				}				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	//在push的时候确定状态码和状态
	private void push(String truePath,int statusCode,String statusString) {  //状态码需要动态设置
		try {
			byte[] tb;   //html页面的字节码
			File file = null;
			//先要从项目的web_content去查找文件,如果有就不使用webapp下的
			boolean isProject = iterateProjectFile(truePath);
			if(isProject) {
				//从本地服务器中把文件读取到字节数组
				StringBuilder psb = new StringBuilder();
				psb.append(project).append("/web_content"+truePath);  //文件的响应要根据路径动态选择
				String path = psb.toString();
				System.out.println("响应的文件路径:"+path);                        //测试路径是否正确
				file = new File(path);
				System.out.println("文件大小:"+file.length());
			}else {
				//从本地服务器中把文件读取到字节数组
				StringBuilder psb = new StringBuilder();
				psb.append(projectPath).append("/webapp"+truePath);  //文件的响应要根据路径动态选择
				String path = psb.toString();
				System.out.println("响应的文件路径:"+path);                        //测试路径是否正确
				file = new File(path);
				System.out.println("文件大小:"+file.length());
			}
					
			//先查看有没有渲染对象,有则渲染没有则正常返回页面
			ClassLoader tClassLoader = Thread.currentThread().getContextClassLoader();
			Class teClass = ClassLoad.getProClassLoader().get(project).loadClass("codeflyer.template_engine.TemplateControl");
			if(teClass != null && truePath.endsWith(".html")) {
				TemplateEngine templateEngine = (TemplateEngine)teClass.newInstance();
				templateEngine.setRequest(request);
				tb = templateEngine.engineEntrance(file);			
			}else {
				fileInputStream = new FileInputStream(file);  //还是先要读成字节数组,因为要获取长度
				bufferedInputStream = new BufferedInputStream(fileInputStream);
				byteArrayOutputStream = new ByteArrayOutputStream((int)file.length());
				int flen = 0;
				byte[] fb = new byte[1024*1024];	
				while((flen = bufferedInputStream.read(fb))!=-1) {  
					byteArrayOutputStream.write(fb,0,flen);
				}
				tb = byteArrayOutputStream.toByteArray();
			}
			
			int contentLen = tb.length;
			System.out.println("文件写到字节数组的大小"+contentLen);			
			//根据文件的后缀名来选择具体的content-type类型
			System.out.println(truePath);
			String[] fileform = truePath.split("\\.");
			System.out.println(fileform[0]);
			String key = fileform[1];
			System.out.println("响应文件的类型:"+key);
			switch (key) {
			case "jpg":
				setContentType("application/x-jpg"); 
				break;
			case "jpeg":
				setContentType("image/jpeg"); 
				break;
			case "png":
				setContentType("application/x-png"); 
				break;
			case "html":
				setContentType("text/html"); 
				break;
			case "ico":
				setContentType("image/x-icon"); 
				break;	
			case "css":
				setContentType("text/css,*/*;q=0.1");
				break;
			default:
			 	setContentType("application/octet-stream");
				break;
			}
			
			//拼接协议
			StringBuilder sbResponse = new StringBuilder();
			sbResponse.append("HTTP/1.1 ").append(statusCode).append(" ").append(statusString).append("\r\n");
			//响应内容应该根据具体请求动态进行
			sbResponse.append("Content-type:").append(contentType).append("\r\n");  //不是和请求的accept相同,根据你自己的文件选择content-type
			//System.out.println(contentType);
			sbResponse.append("Content-length:").append(contentLen).append("\r\n");
			sbResponse.append("\r\n");
			byte[] bResponse = sbResponse.toString().getBytes();
			
			//不通过字符流的方式发送数据,而是用字节流的方式,字符流会导致字节码数组失真
			//把响应信息和数据的字节数组进行拼接
			byte[] total = new byte[bResponse.length+tb.length]; 
			System.arraycopy(bResponse, 0, total, 0, bResponse.length);
			System.arraycopy(tb, 0, total, bResponse.length, tb.length);
			
			System.out.println("合并的总字节数:"+total.length);
			//发送给浏览器
			bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
			bufferedOutputStream.write(total);
			bufferedOutputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {  //响应成功或者出现异常都要关闭所有的流
			
			try {
				if(bufferedOutputStream!=null) {
					bufferedOutputStream.close();
				}			
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			try {
				if(byteArrayOutputStream!=null) {
					byteArrayOutputStream.close();
				}				
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			
			try {
				if(bufferedInputStream!=null) {
					bufferedInputStream.close();
				}			
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			
			try {
				if(fileInputStream!=null){
					fileInputStream.close();
				}				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}

}


